Gah - a solution with more questions. – EntropicLqd
Within clause
The within clause is an optional part of class declarations that defines the class to be an inner class. It was introduced in Unreal Engine 2 and forces instances of a class to be created within instances of another class.
Inner classes declared with a within clause can access members of the specified outer class without having to qualify the access with the Outer property declared in Object(U2, U2XMP, UE2Runtime, UT2003, UT2004, UDK, UT3). If a member of the inner class hides a member of the specified outer class, the access can be qualified with the Outer property without having to typecast it as the Outer property is guaranteed to reference an instance of the outer class or one of its subclasses.
Syntax[edit]
To declare a class (which extends another class) to be an inner class of a third class, use the following class declaration:
class ClassName extends ParentClass within OuterClass;
Any optional class modifiers can be added after the within class name as usual.
The following rules apply:
- The parent class must not be the class Actor(U2, U2XMP, UE2Runtime, UT2003, UT2004, UDK, UT3) or any of its subclasses.
- The parent class may be declared within another class. In that case, the outer class must be a subclass of the parent's outer class. This basically reduces the allowed range of outer classes without causing any inconsistencies.
- The within clause may be omitted, actually only few classes use it. In this case the parent's outer class is inherited. (The implicit outer class of the Object class is Object.)
- The parent class and the outer class may be the same class.
Example usage[edit]
Some examples of inner classes are CheatManager(U2, U2XMP, UE2Runtime, UT2003, UT2004, UDK, UT3) and PlayerInput(U2, U2XMP, UE2Runtime, UT2003, UT2004, UDK, UT3), both declared within PlayerController(U2, U2XMP, UE2Runtime, UT2003, UT2004, UDK, UT3). An example of "narrowing" the valid range of outer classes is GamePlayerInput - it reduces the valid outer classes from all PlayerControllers to only instances of GamePlayerController and its subclasses.
As a more generic example, consider the following scenario:
class A extends Something; var AHelper Helper; function CreateHelper() { Helper = new(Self) class'AHelper'; } function Help() { // do something helpful }
class B extends A; var BHelper BetterHelper; function CreateHelper() { BetterHelper = new(Self) class'BetterHelper'; Helper = BetterHelper; } function BetterHelp() { // do something even more helpful }
The parameter of the new operator sets the created object's Outer variable and Self is the object executing that code, i.e. objects of class A and B respectively. The helper object are supposed to call the Help and BetterHelp functions of their corresponding outer objects. Without the within clause, that might look as follows:
class AHelper extends Object; function TryToHelp() { A(Outer).Help(); }
class BetterHelper extends AHelper; function TryToHelp() { B(Outer).BetterHelp(); }
But there are still problems. Not only is the typecasted access to the Outer variable a bit clunky, but there's also no guarantee it actually always points to an object of type A or B respectively. These can be resolved using a within clause:
class AHelper extends Object within A; function TryToHelp() { Outer.Help(); }
class BetterHelper extends AHelper within B; function TryToHelp() { Outer.BetterHelp(); }
The within clauses guarantee that AHelper and BetterHelper objects are definitely created with an A or B object as first parameter of the new operator respectively. You can now use the Outer variable without typecasting it.
In fact, when the compiler finds any (unqualified) access to variables or functions not declared in the class or inherited from any of the parent classes, it tries accessing the variable or function through the Outer variable instead for classes declared within other classes. That means you could actually also write:
class AHelper extends Object within A; function TryToHelp() { Help(); }
class BetterHelper extends AHelper within B; function TryToHelp() { BetterHelp(); }
It will have absolutely the same result as the implementation that uses Outer.
to call those functions. Epic Games programmers use this extensively, but you should keep in mind that it slightly reduces code readability because you no longer see that you actually call functions (or access variables) from a different object.
Declarations | Preprocessor • Classes • Interfaces • Cpptext • Constants • Enums • Structs • Variables (Metadata) • Replication block • Operators • Delegates • Functions • States • Defaultproperties (Subobjects) |
---|---|
Types | bool • byte • float • int • name • string • Object • Class • Enums • Structs (Vector ⋅ Rotator ⋅ Quat ⋅ Color) • Static arrays • Dynamic arrays • Delegates • Typecasting |
Literals | Boolean • Float • Integer • Names • Objects (None ⋅ Self) • Vectors • Rotators • Strings |
Flow | GoTo • If • Assert • Return • Stop • Switch • While • Do...Until • For • ForEach • Break • Continue |
Specifiers | Super • Global • Static • Default • Const |
UnrealScript | Syntax • .UC • .UCI • .UPKG • Comments • #directives • Native |